/**
 * 
 */
package lt.ruta.data;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInput;
import java.io.DataInputStream;
import java.io.DataOutput;
import java.io.DataOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;

import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;
import javax.microedition.rms.RecordStoreNotOpenException;

import lt.ruta.Utils;


/**
 * @author Vincentas
 *
 */
public class Table {

	private final String name;
	
	public Table() {
		this("table");
	}
	
	public Table(String name) {
		this.name = name;
	}
	
	/**
	 * @param record
	 */
	public void addRecord(Record record) {
		try {
			RecordStore recordStore = RecordStore.openRecordStore(name, true);
			try {
				byte[] data = write(record);
				recordStore.addRecord(data, 0, data.length);
			} finally {
				recordStore.closeRecordStore();
			}
		} catch (RecordStoreException recordStoreException) {
			
		}	
	}
	
	public Record getRecord(int index) {
		try {
			RecordStore recordStore = RecordStore.openRecordStore(name, true);
			try {
				return read(recordStore.getRecord(index + 1));
			} finally {
				recordStore.closeRecordStore();
			}
		} catch (RecordStoreException recordStoreException) {
			return null;
		}
	}
	
	public int getSize() {
		try {
			RecordStore recordStore = RecordStore.openRecordStore(name, true);
			try {
				return recordStore.getNumRecords();
			} finally {
				recordStore.closeRecordStore();
			}
		} catch (RecordStoreException recordStoreException) {
			return 0;
		}		
	}
	
	public void clear() {
		try {
			RecordStore.deleteRecordStore(name);
		} catch (RecordStoreException e) {
			e.printStackTrace();
		}		
	}
	
	private Record read(byte[] data) {
		ByteArrayInputStream bais = new ByteArrayInputStream(data);
		DataInput dataInput = new DataInputStream(bais);
		
		try {
			String amount = dataInput.readUTF();
			String description = dataInput.readUTF();
			long timestamp = dataInput.readLong();
			
			return new Record(amount, description, timestamp);
		} catch (IOException exception) {
			return null;
		}
	}
	
	private byte[] write(Record record) {
		ByteArrayOutputStream baos = new ByteArrayOutputStream();
		DataOutput dataOutput = new DataOutputStream(baos);
		
		try {
			dataOutput.writeUTF(record.getAmount());
			dataOutput.writeUTF(record.getDescription());
			dataOutput.writeLong(record.getTimestamp());
		} catch (IOException exception) {
			exception.printStackTrace();
		}
		
		return baos.toByteArray();

	}
	
	public void writeTo(OutputStream outputStream) {
		PrintStream printStream = new PrintStream(outputStream);
		
		String header = Utils.csvJoin(new String[] {
				"Date",
				"Description",
				"Amount"
		});
		
		printStream.println(header);
		
		TableIterator iterator = getIterator();
		try {
			while (iterator.hasNext()) {
				Record record = iterator.getNext();			
				String row = Utils.csvJoin(new String[] {
						Utils.formatDate(record.getTimestamp()),
						record.getDescription(),
						record.getAmount()
				});
				
				printStream.println(row);
			}
		} finally {
			iterator.destroy();
		}
		
		printStream.flush();
	}
	
	public TableIterator getIterator() {

		try {
			final RecordStore recordStore = RecordStore.openRecordStore(name, false);
			final RecordEnumeration enumeration = recordStore.enumerateRecords(null, null, false);
	
			return new TableIterator() {
				
				public boolean hasNext() {
					return enumeration.hasNextElement();
				}
				
				public boolean hasPrevious() {
					return enumeration.hasPreviousElement();
				}
				
				public synchronized Record getPrevious() {
					try {
						return read(enumeration.previousRecord());
					} catch (InvalidRecordIDException e) {
						e.printStackTrace();
					} catch (RecordStoreNotOpenException e) {
						e.printStackTrace();
					} catch (RecordStoreException e) {
						e.printStackTrace();
					}
					
					return null;
				}
				
				public synchronized Record getNext() {
					try {
						return read(enumeration.nextRecord());
					} catch (InvalidRecordIDException e) {
						e.printStackTrace();
					} catch (RecordStoreNotOpenException e) {
						e.printStackTrace();
					} catch (RecordStoreException e) {
						e.printStackTrace();
					}
					
					return null;
				}
				
				public void destroy() {
					enumeration.destroy();
					
					try {
						recordStore.closeRecordStore();
					} catch (RecordStoreNotOpenException e) {
						e.printStackTrace();
					} catch (RecordStoreException e) {
						e.printStackTrace();
					}
				}
			};
		} catch (RecordStoreNotFoundException e) {
			e.printStackTrace();
		} catch (RecordStoreFullException e) {
			e.printStackTrace();
		} catch (RecordStoreException e) {
			e.printStackTrace();
		}

		/*
		 * Return empty iterator if unable to open record store 
		 */
		return new TableIterator() {
		
			public boolean hasNext() {
				return false;
			}
		
			public Record getNext() {
				return null;
			}

			public void destroy() {
				
			}

			public Record getPrevious() {
				return null;
			}

			public boolean hasPrevious() {
				return false;
			}
		};
	}
}
